package code.HAOJianguo;

import java.io.BufferedWriter;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;

import client.domainModels.Node.Node;
import client.domainModels.NodeImpl.AbstractNode;
import client.domainModels.NodeImpl.LogFile;

public class ContractNetModelisation {
	private static ArrayList<String> agents;
	private static ArrayList<String> cases;
	// Contract Net Protocol Definition
	private static ArrayList<String> CFP = new ArrayList<String>() {
		{
			add("cfp");
			add("call");
		};
	};

	private static ArrayList<String> REFUSE = new ArrayList<String>() {
		{
			add("refuse");
			add("absence");
		};
	};

	private static ArrayList<String> PROPOSE = new ArrayList<String>() {
		{
			add("propose");
			add("present");
		};
	};

	private static ArrayList<String> REJECT_PROPOSE = new ArrayList<String>() {
		{
			add("reject_proposal");
		};
	};

	private static ArrayList<String> ACCEPT_PROPOSE = new ArrayList<String>() {
		{
			add("accept_proposal");
			add("accept_propose");
		};
	};

	private static ArrayList<String> FAILURE = new ArrayList<String>() {
		{
			add("failure");
		};
	};

	private static ArrayList<String> INFORM_DONE = new ArrayList<String>() {
		{
			add("inform_done");
			add("success");
		};
	};

	private static ArrayList<String> INFORM_RESULT = new ArrayList<String>() {
		{
			add("inform_result");
			add("later");
		};
	};

	/**
	 * @author HAO Jianguo This is a function to analyse all the activity of
	 *         contract net protocol.
	 * */
	@SuppressWarnings("unchecked")
	public static void analyseContractNetInteraction(LogFile log) {
		agents = new ArrayList<String>();
		cases = new ArrayList<String>();

		int agentAmount = getAgentAmount(log);
		ArrayList[][] interactionMatrix = getContractNetInteractionMatrix(log,
				agentAmount);

		// initial final matrix
		Integer[][] netDrawMatrix = new Integer[agentAmount][agentAmount];
		for (int i = 0; i < agentAmount; i++) {
			for (int j = 0; j < agentAmount; j++) {
				netDrawMatrix[i][j] = 0;
			}
		}

		for (int i = 0; i < agentAmount; i++) {
			for (int j = 0; j < agentAmount; j++) {
				String conversationOfActor = "";
				if (interactionMatrix[i][j] == null
						|| interactionMatrix[j][i] == null)
					continue;
				// test if the first tester has cfp action, it's sure
				// that it is actor.

				// interactionMatrix[i][j] is a object of ArrayList
				for (int k = 0; k < interactionMatrix[i][j].size(); k++) {
					boolean isActor = false;
					
					InteractionMap im = (InteractionMap) interactionMatrix[i][j]
							.get(k);
					if (CFP.contains(im.getPerformative().toLowerCase())) {
						isActor = true;
						conversationOfActor = im.getConversation();
					}

					// The first tester is actor
					if (isActor) {
						if (existSatisfactEvent(REFUSE,
								interactionMatrix[j][i], conversationOfActor)) {
							netDrawMatrix[i][j] += 1;
							// if he found Contract Net Protocol, quit.
							continue;
						}

						// if interactor not refused
						// if interactor has propose action
						for (int m = 0; m < interactionMatrix[j][i].size(); m++) {
							InteractionMap loop_a = (InteractionMap) interactionMatrix[j][i]
									.get(m);
							if (PROPOSE.contains(loop_a.getPerformative())) {
								// System.out.println("HAS PROPOSE");
								// tester has reject propose, yes
								if (existSatisfactEvent(REJECT_PROPOSE,
										interactionMatrix[i][j],
										conversationOfActor)) {
									netDrawMatrix[i][j] += 1;
									break;
									// tester has accept propose
								} else if (existSatisfactEvent(ACCEPT_PROPOSE,
										interactionMatrix[i][j],
										conversationOfActor)) {
									// his interactor has failure or inform
									// propose, yes
									System.out.println("ACCEPT");
									if (existSatisfactEvent(FAILURE,
											interactionMatrix[j][i],
											conversationOfActor)
											|| existSatisfactEvent(INFORM_DONE,
													interactionMatrix[j][i],
													conversationOfActor)
											|| existSatisfactEvent(
													INFORM_RESULT,
													interactionMatrix[j][i],
													conversationOfActor)) {
										netDrawMatrix[i][j] += 1;
										break;
									}
								}
							}
						}
					}
				}
			}
		}

		/********* TEST **********/
		for (int i = 0; i < netDrawMatrix.length; i++) {
			System.out.print(agents.get(i) + "  ");
		}
		System.out.println();
		for (int i = 0; i < netDrawMatrix.length; i++) {
			for (int j = 0; j < netDrawMatrix.length; j++) {
				System.out.print(netDrawMatrix[i][j] + " ");
			}
			System.out.println();
		}
		/********* TEST **********/
		createFileVNA(netDrawMatrix, "./contract_net.vna");
		createBatchFile("./batch_contract.txt", "contract_net.vna");
	}

	//
	public static int getCaseAmount(LogFile log) {
		int caseAmount = 0;
		String[] nodes = log.getChildNodeNames();
		// Get leaves nodes of root node.
		for (int i = 0; i < nodes.length; i++) {
			// name of root node's leaves nodes.
			String root_leaf = nodes[i];
			// get cases - root node's leaves nodes.
			ArrayList<AbstractNode> cases = log.getChildNodes(root_leaf);
			// iterate cases and get his leaves nodes'object.
			for (Iterator<AbstractNode> ii = cases.iterator(); ii.hasNext();) {
				// get each event - cases' leaves nodes object.
				Node case_ = ii.next();
				ContractNetModelisation.cases.add(case_.getID());
			}
			caseAmount = cases.size();
		}
		// create a matrix and the size of matrix is amount of agent * amount of
		// agent.
		return caseAmount;
	}

	/**
	 * This foncion will get a matrix which contains all the interaction in the
	 * file log. Each line of the matrix is an actor, and each row of the matrix
	 * is a receiver.
	 * 
	 * @author HAO Jianguo
	 * @param log
	 *            The object of LogFile to analyse.
	 * @param agentAmount
	 *            The amount of agent(actor, participant).
	 * @return interactionMatrix The matrix which contains all the interaction
	 *         in the file log.
	 * */
	private static ArrayList[][] getContractNetInteractionMatrix(LogFile log,
			int agentAmount) {
		ArrayList<InteractionMap>[][] interactionMatrix = new ArrayList[agentAmount][agentAmount];
		String[] nodes = log.getChildNodeNames();
		// Part 2 - analyse the matrix of delegate
		// Get leaves nodes of root node.
		for (int i = 0; i < nodes.length; i++) {
			// name of root node's leaves nodes.
			String root_leaf = nodes[i];
			// get cases - root node's leaves nodes.
			ArrayList<AbstractNode> cases = log.getChildNodes(root_leaf);
			// iterate cases and get his leaves nodes'object.
			for (Iterator<AbstractNode> ii = cases.iterator(); ii.hasNext();) {
				// get each event - cases' leaves nodes object.
				Node case_ = ii.next();
				ArrayList<AbstractNode> events = case_
						.getChildNames_childObjs().get("Event");
				for (Iterator<AbstractNode> iii = events.iterator(); iii
						.hasNext();) {
					Node event = iii.next();
					// get actor, receivers - event's leaves nodes.
					ArrayList<AbstractNode> actors = event
							.getChildNames_childObjs().get("Actor");
					ArrayList<AbstractNode> performative = event
							.getChildNames_childObjs().get("Performatif");
					ArrayList<AbstractNode> conversations = event
							.getChildNames_childObjs().get("Conversation");
					ArrayList<AbstractNode> receiverss = event
							.getChildNames_childObjs().get("Receivers");
					ArrayList<AbstractNode> receivers = receiverss.get(0)
							.getChildNames_childObjs().get("Receiver");
					// The collection (agents) of actor and receivers is empty
					// or not
					String actorID = actors.get(0).getID();
					int indexOfActor = agents.indexOf(actorID);

					// iterate each receiver to the collection
					for (Iterator<AbstractNode> iiii = receivers.iterator(); iiii
							.hasNext();) {
						Node receiver = iiii.next();
						String receiverName = receiver.getID();
						int indexOfReceiver = agents.indexOf(receiverName);
						String performatif = performative.get(0).getID();
						String conversation = conversations.get(0).getID();
						/*
						 * estimate that if that element of matrix is null or
						 * not. if null, create a object.
						 */
						if (interactionMatrix[indexOfActor][indexOfReceiver] == null) {
							interactionMatrix[indexOfActor][indexOfReceiver] = new ArrayList<InteractionMap>();
						}
						// add the performatif in to matrix.
						InteractionMap im = new InteractionMap(performatif,
								conversation);
						interactionMatrix[indexOfActor][indexOfReceiver]
								.add(im);
					}
				}
			}
		}
		return interactionMatrix;
	}

	// public static boolean isContainKeyword(ArrayList<String> keywords,
	// ArrayList<InteractionMap> interactionMaps) {
	// // this methode is not good, because we know it exist, but we don't know
	// // which one. So we can't get the one's conversation.
	// boolean res = false;
	// for (int i = 0; i < interactionMaps.size(); i++) {
	// if (keywords.contains(interactionMaps.get(i).getPerformative()
	// .toLowerCase())) {
	// res = true;
	// }
	// }
	// return res;
	// }

	public static boolean existSatisfactEvent(ArrayList<String> keywords,
			ArrayList<InteractionMap> im, String conversation) {
		for (int i = 0; i < im.size(); i++) {
			if (keywords.contains(im.get(i).getPerformative())
					&& im.get(i).getConversation()
							.equalsIgnoreCase(conversation)) {
				return true;
			}
		}
		return false;
	}

	private static int getAgentAmount(LogFile log) {
		int agentAmount = 0;
		String[] nodes = log.getChildNodeNames();
		// Get leaves nodes of root node.
		for (int i = 0; i < nodes.length; i++) {
			// name of root node's leaves nodes.
			String root_leaf = nodes[i];
			// get cases - root node's leaves nodes.
			ArrayList<AbstractNode> cases = log.getChildNodes(root_leaf);
			// iterate cases and get his leaves nodes'object.
			for (Iterator<AbstractNode> ii = cases.iterator(); ii.hasNext();) {
				// get each event - cases' leaves nodes object.
				Node case_ = ii.next();
				ArrayList<AbstractNode> events = case_
						.getChildNames_childObjs().get("Event");
				for (Iterator<AbstractNode> iii = events.iterator(); iii
						.hasNext();) {
					Node event = iii.next();
					// get actor, receivers - event's leaves nodes.
					ArrayList<AbstractNode> actors = event
							.getChildNames_childObjs().get("Actor");
					ArrayList<AbstractNode> receiverss = event
							.getChildNames_childObjs().get("Receivers");
					// The collection (agents) of actor and receivers is empty
					// or
					// not
					String actorID = actors.get(0).getID();
					if (agents.isEmpty()) {
						// add actor to the collection
						agents.add(actorID);
					} else {
						if (!agents.contains(actorID)) {
							agents.add(actorID);
						}
					}
					// add each receiver to the collection
					ArrayList<AbstractNode> receivers = receiverss.get(0)
							.getChildNames_childObjs().get("Receiver");
					for (Iterator<AbstractNode> iiii = receivers.iterator(); iiii
							.hasNext();) {
						Node receiver = iiii.next();
						String receiverName = receiver.getID();
						if (!agents.contains(receiverName)) {
							agents.add(receiverName);
						}
					}
				}
			}
		}
		// create a matrix and the size of matrix is amount of agent * amount of
		// agent.
		agentAmount = agents.size();
		return agentAmount;
	}

	/**
	 * This fonction will create a VNA file to be used in NetDraw
	 * 
	 * @author HAO Jianguo
	 * */
	public static void createFileVNA(Integer[][] matrix, String filePath) {
		File pointVNA = new File(filePath);
		try {
			FileWriter fw = new FileWriter(pointVNA);
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write("*Node data" + "\n");
			bw.write("*Node properties" + "\n");
			bw.write("*Tie data" + "\n");
			bw.write("ACTOR RECEIVER \"" + "Propose Interaction" + "\"" + "\n");
			for (int i = 0; i < matrix.length; i++) {
				for (int j = 0; j < matrix.length; j++) {
					if (matrix[i][j] != 0) {
						String actor = agents.get(i);
						String receiver = agents.get(j);
						bw.write("\"" + actor + "\"");
						bw.write(" ");
						bw.write("\"" + receiver + "\"");
						bw.write(" ");
						bw.write(""+matrix[i][j]+"");
						bw.write("\n");
					}
				}
			}
			bw.flush();
			bw.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static void createBatchFile(String filePath, String vnaName) {
		File pointBatch = new File(filePath);
		try {
			FileWriter fw = new FileWriter(pointBatch);
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write("Loadvna " + vnaName + "\n");
			bw.write("runlayout" + "\n");
			bw.flush();
			bw.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
